/*
 * CCVisu is a tool for visual graph clustering
 * and general force-directed graph layout.
 * This file is part of CCVisu.
 *
 * Copyright (C) 2005-2011  Dirk Beyer
 *
 * CCVisu is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * CCVisu is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with CCVisu; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Please find the GNU Lesser General Public License in file
 * license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
 *
 * Dirk Beyer    (firstname.lastname@uni-passau.de)
 * University of Passau, Bavaria, Germany
 */
package org.sosy_lab.ccvisu.ui;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.JColorChooser;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;

import org.sosy_lab.ccvisu.graph.GraphData;
import org.sosy_lab.ccvisu.graph.GraphEdge;
import org.sosy_lab.ccvisu.graph.GraphVertex;
import org.sosy_lab.ccvisu.ui.FrameDisplay.GraphCanvas;
import org.sosy_lab.util.Colors;

public class FrameDisplayMenu implements ActionListener {
  private GraphCanvas         frameDisplayCanvas;

  private GraphData           graph;

  private JPopupMenu          featureMenu;
  private JMenuItem           menuItemZoomOut;
  private JMenuItem           menuItemVertexColor;
  private JMenuItem           menuItemVertexToBackground;
  private JMenuItem           menuItemHideUnrelatedVertices;
  private JMenuItem           menuItemResetHighlighting;
  private List<JComponent>    menuItemsRelations = new ArrayList<JComponent>();

  private Map<Integer, Color> edgeColors         = new HashMap<Integer, Color>();
  private Map<Integer, Color> vertexColors       = new HashMap<Integer, Color>();

  private GraphVertex         featureVertex      = null;

  public FrameDisplayMenu(GraphCanvas frameDisplay, GraphData graph) {
    this.frameDisplayCanvas = frameDisplay;
    this.graph = graph;
  }

  // Handle action on the MenuItems
  @Override
  public void actionPerformed(ActionEvent actionevent) {
    if (actionevent.getSource() == menuItemZoomOut) {
      frameDisplayCanvas.zoomOut();

    } else if (actionevent.getSource() == menuItemVertexColor) {

      Color selectedColor = JColorChooser.showDialog(frameDisplayCanvas, "Select Color", Color.black);

      featureVertex.setColor(selectedColor);
      for (GraphEdge e : frameDisplayCanvas.getWriter().getGraphData().getAdjacent(featureVertex, "Introduces")) {
        e.getTarget().setColor(selectedColor);
      }

    } else if (actionevent.getSource() == menuItemHideUnrelatedVertices) {
      hideEdges();

      for (GraphEdge e : frameDisplayCanvas.getWriter().getGraphData().getEdges()) {
        e.setShowEdge(false);
      }

      Set<GraphVertex> visibleVertices = new HashSet<GraphVertex>();
      visibleVertices.add(featureVertex);
      for (GraphEdge e : frameDisplayCanvas.getWriter().getGraphData().getAdjacent(featureVertex, ".*")) {
        visibleVertices.add(e.getTarget());
        e.setShowEdge(true);
      }
      for (GraphVertex vertex : frameDisplayCanvas.getWriter().getGraphData().getVertices()) {
        if (!visibleVertices.contains(vertex)) {
          vertex.setShowVertex(false);
        }
      }

    } else if (actionevent.getSource() == menuItemVertexToBackground) {
      // TODO: ??
      System.out.println("move vertex to background");

    } else if (actionevent.getSource() == menuItemResetHighlighting) {
      for (GraphEdge e : frameDisplayCanvas.getWriter().getGraphData().getEdges()) {
        e.setShowEdge(false);
      }

      for (Integer id : edgeColors.keySet()) {
        GraphEdge edge = frameDisplayCanvas.getWriter().getGraphData().getEdgeById(id);
        if (edge != null) {
          edge.setColor(edgeColors.get(id));
        }
      }
      for (Integer id : vertexColors.keySet()) {
        GraphVertex vertex = frameDisplayCanvas.getWriter().getGraphData().getVertexById(id);
        if (vertex != null) {
          vertex.setColor(vertexColors.get(id));
        }
      }

      resetHighlightingCache();

    } else {
      // Highlight chosen relation
      if (actionevent.getSource() instanceof JMenuItem) {
        String relName = ((JMenuItem) actionevent.getSource()).getName();
        hideEdges();

        for (GraphEdge e : frameDisplayCanvas.getWriter().getGraphData().getAdjacent(featureVertex, relName)) {
          e.setShowEdge(true);

          if (!edgeColors.containsKey(e.getId())) {
            edgeColors.put(e.getId(), e.getColor());
          }
          e.setColor(Colors.get("red"));

          if (!vertexColors.containsKey(e.getTarget().getId())) {
            vertexColors.put(e.getTarget().getId(), e.getTarget().getColor());
          }
          e.getTarget().setColor(Colors.get("red"));
        }
      }
    }

    graph.notifyAboutLayoutChange(new EventObject(this));
  }

  public void resetHighlightingCache() {
    edgeColors.clear();
    vertexColors.clear();
  }

  private void hideEdges() {
    frameDisplayCanvas.getWriter().setShowEdges(false);
  }

  public JPopupMenu createMenu() {
    featureMenu = new JPopupMenu();

    menuItemZoomOut = new JMenuItem("Zoom out / Reset vertex restrictions");
    menuItemZoomOut.addActionListener(this);
    featureMenu.add(menuItemZoomOut);

    menuItemVertexColor = new JMenuItem("Select vertex color");
    menuItemVertexColor.addActionListener(this);
    featureMenu.add(menuItemVertexColor);

    menuItemHideUnrelatedVertices = new JMenuItem("Hide unrelated vertices");
    menuItemHideUnrelatedVertices.addActionListener(this);
    featureMenu.add(menuItemHideUnrelatedVertices);

    menuItemVertexToBackground = new JMenuItem("Move vertex to background");
    menuItemVertexToBackground.addActionListener(this);
    featureMenu.add(menuItemVertexToBackground);

    JSeparator separator = new JSeparator();
    featureMenu.add(separator);

    menuItemResetHighlighting = new JMenuItem("Reset highlighting");
    menuItemResetHighlighting.addActionListener(this);
    featureMenu.add(menuItemResetHighlighting);

    return featureMenu;
  }

  public void updateAndShowMenu(MouseEvent evt, Set<String> vertexRelations, GraphVertex featureVertex) {
    // Remove out-dated menu items
    for (JComponent menuItem : menuItemsRelations) {
      featureMenu.remove(menuItem);
    }

    if (vertexRelations.size() > 0) {
      for (String relName : vertexRelations) {
        JMenuItem relMenuItem = new JMenuItem("Highlight " + relName + " relations");
        relMenuItem.setName(relName);
        relMenuItem.addActionListener(this);

        menuItemsRelations.add(relMenuItem);
        featureMenu.add(relMenuItem);
      }
    }

    menuItemVertexColor.setEnabled(featureVertex != null);
    menuItemHideUnrelatedVertices.setEnabled((featureVertex != null)
        && (frameDisplayCanvas.getWriter().getGraphData().isEdgesAvailable()));
    menuItemVertexToBackground.setEnabled(featureVertex != null);

    menuItemResetHighlighting.setEnabled((edgeColors.size() > 0) && (vertexColors.size() > 0));

    this.featureVertex = featureVertex;

    featureMenu.show(evt.getComponent(), evt.getX(), evt.getY());
  }
}
